home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / Python 1.1 / Lib / bdb.py < prev    next >
Encoding:
Python Source  |  1994-08-01  |  8.7 KB  |  336 lines  |  [TEXT/R*ch]

  1. # A generic Python debugger base class.
  2. # This class takes care of details of the trace facility;
  3. # a derived class should implement user interaction.
  4. # There are two debuggers based upon this:
  5. # 'pdb', a text-oriented debugger not unlike dbx or gdb;
  6. # and 'wdb', a window-oriented debugger.
  7. # And of course... you can roll your own!
  8.  
  9. import sys
  10.  
  11. BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
  12.  
  13.  
  14. class Bdb: # Basic Debugger
  15.     
  16.     def __init__(self):
  17.         self.breaks = {}
  18.     
  19.     def reset(self):
  20.         import linecache
  21.         linecache.checkcache()
  22.         self.botframe = None
  23.         self.stopframe = None
  24.         self.returnframe = None
  25.         self.quitting = 0
  26.     
  27.     def trace_dispatch(self, frame, event, arg):
  28.         if self.quitting:
  29.             return # None
  30.         if event == 'line':
  31.             return self.dispatch_line(frame)
  32.         if event == 'call':
  33.             return self.dispatch_call(frame, arg)
  34.         if event == 'return':
  35.             return self.dispatch_return(frame, arg)
  36.         if event == 'exception':
  37.             return self.dispatch_exception(frame, arg)
  38.         print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  39.         return self.trace_dispatch
  40.     
  41.     def dispatch_line(self, frame):
  42.         if self.stop_here(frame) or self.break_here(frame):
  43.             self.user_line(frame)
  44.             if self.quitting: raise BdbQuit
  45.         return self.trace_dispatch
  46.     
  47.     def dispatch_call(self, frame, arg):
  48.         frame.f_locals['__args__'] = arg
  49.         if self.botframe is None:
  50.             # First call of dispatch since reset()
  51.             self.botframe = frame
  52.             return self.trace_dispatch
  53.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  54.             # No need to trace this function
  55.             return # None
  56.         self.user_call(frame, arg)
  57.         if self.quitting: raise BdbQuit
  58.         return self.trace_dispatch
  59.     
  60.     def dispatch_return(self, frame, arg):
  61.         if self.stop_here(frame) or frame == self.returnframe:
  62.             self.user_return(frame, arg)
  63.             if self.quitting: raise BdbQuit
  64.     
  65.     def dispatch_exception(self, frame, arg):
  66.         if self.stop_here(frame):
  67.             self.user_exception(frame, arg)
  68.             if self.quitting: raise BdbQuit
  69.         return self.trace_dispatch
  70.     
  71.     # Normally derived classes don't override the following
  72.     # methods, but they may if they want to redefine the
  73.     # definition of stopping and breakpoints.
  74.     
  75.     def stop_here(self, frame):
  76.         if self.stopframe is None:
  77.             return 1
  78.         if frame is self.stopframe:
  79.             return 1
  80.         while frame is not None and frame is not self.stopframe:
  81.             if frame is self.botframe:
  82.                 return 1
  83.             frame = frame.f_back
  84.         return 0
  85.     
  86.     def break_here(self, frame):
  87.         if not self.breaks.has_key(frame.f_code.co_filename):
  88.             return 0
  89.         if not frame.f_lineno in \
  90.                 self.breaks[frame.f_code.co_filename]:
  91.             return 0
  92.         return 1
  93.     
  94.     def break_anywhere(self, frame):
  95.         return self.breaks.has_key(frame.f_code.co_filename)
  96.     
  97.     # Derived classes should override the user_* methods
  98.     # to gain control.
  99.     
  100.     def user_call(self, frame, argument_list):
  101.         # This method is called when there is the remote possibility
  102.         # that we ever need to stop in this function
  103.         pass
  104.     
  105.     def user_line(self, frame):
  106.         # This method is called when we stop or break at this line
  107.         pass
  108.     
  109.     def user_return(self, frame, return_value):
  110.         # This method is called when a return trap is set here
  111.         pass
  112.     
  113.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  114.         # This method is called if an exception occurs,
  115.         # but only if we are to stop at or just below this level
  116.         pass
  117.     
  118.     # Derived classes and clients can call the following methods
  119.     # to affect the stepping state.
  120.     
  121.     def set_step(self):
  122.         # Stop after one line of code
  123.         self.stopframe = None
  124.         self.returnframe = None
  125.         self.quitting = 0
  126.     
  127.     def set_next(self, frame):
  128.         # Stop on the next line in or below the given frame
  129.         self.stopframe = frame
  130.         self.returnframe = None
  131.         self.quitting = 0
  132.     
  133.     def set_return(self, frame):
  134.         # Stop when returning from the given frame
  135.         self.stopframe = frame.f_back
  136.         self.returnframe = frame
  137.         self.quitting = 0
  138.     
  139.     def set_trace(self):
  140.         # Start debugging from here
  141.         try:
  142.             1 + ''
  143.         except:
  144.             frame = sys.exc_traceback.tb_frame.f_back
  145.         self.reset()
  146.         while frame:
  147.             frame.f_trace = self.trace_dispatch
  148.             self.botframe = frame
  149.             frame = frame.f_back
  150.         self.set_step()
  151.         sys.settrace(self.trace_dispatch)
  152.  
  153.     def set_continue(self):
  154.         # Don't stop except at breakpoints or when finished
  155.         self.stopframe = self.botframe
  156.         self.returnframe = None
  157.         self.quitting = 0
  158.         if not self.breaks:
  159.             # no breakpoints; run without debugger overhead
  160.             sys.settrace(None)
  161.             try:
  162.                 1 + ''    # raise an exception
  163.             except:
  164.                 frame = sys.exc_traceback.tb_frame.f_back
  165.             while frame and frame is not self.botframe:
  166.                 del frame.f_trace
  167.                 frame = frame.f_back
  168.     
  169.     def set_quit(self):
  170.         self.stopframe = self.botframe
  171.         self.returnframe = None
  172.         self.quitting = 1
  173.         sys.settrace(None)
  174.     
  175.     # Derived classes and clients can call the following methods
  176.     # to manipulate breakpoints.  These methods return an
  177.     # error message is something went wrong, None if all is well.
  178.     # Call self.get_*break*() to see the breakpoints.
  179.     
  180.     def set_break(self, filename, lineno):
  181.         import linecache # Import as late as possible
  182.         line = linecache.getline(filename, lineno)
  183.         if not line:
  184.             return 'That line does not exist!'
  185.         if not self.breaks.has_key(filename):
  186.             self.breaks[filename] = []
  187.         list = self.breaks[filename]
  188.         if lineno in list:
  189.             return 'There is already a breakpoint there!'
  190.         list.append(lineno)
  191.     
  192.     def clear_break(self, filename, lineno):
  193.         if not self.breaks.has_key(filename):
  194.             return 'There are no breakpoints in that file!'
  195.         if lineno not in self.breaks[filename]:
  196.             return 'There is no breakpoint there!'
  197.         self.breaks[filename].remove(lineno)
  198.         if not self.breaks[filename]:
  199.             del self.breaks[filename]
  200.     
  201.     def clear_all_file_breaks(self, filename):
  202.         if not self.breaks.has_key(filename):
  203.             return 'There are no breakpoints in that file!'
  204.         del self.breaks[filename]
  205.     
  206.     def clear_all_breaks(self):
  207.         if not self.breaks:
  208.             return 'There are no breakpoints!'
  209.         self.breaks = {}
  210.     
  211.     def get_break(self, filename, lineno):
  212.         return self.breaks.has_key(filename) and \
  213.             lineno in self.breaks[filename]
  214.     
  215.     def get_file_breaks(self, filename):
  216.         if self.breaks.has_key(filename):
  217.             return self.breaks[filename]
  218.         else:
  219.             return []
  220.     
  221.     def get_all_breaks(self):
  222.         return self.breaks
  223.     
  224.     # Derived classes and clients can call the following method
  225.     # to get a data structure representing a stack trace.
  226.     
  227.     def get_stack(self, f, t):
  228.         stack = []
  229.         if t and t.tb_frame is f:
  230.             t = t.tb_next
  231.         while f is not None:
  232.             stack.append((f, f.f_lineno))
  233.             if f is self.botframe:
  234.                 break
  235.             f = f.f_back
  236.         stack.reverse()
  237.         i = max(0, len(stack) - 1)
  238.         while t is not None:
  239.             stack.append((t.tb_frame, t.tb_lineno))
  240.             t = t.tb_next
  241.         return stack, i
  242.     
  243.     # 
  244.     
  245.     def format_stack_entry(self, frame_lineno):
  246.         import linecache, repr, string
  247.         frame, lineno = frame_lineno
  248.         filename = frame.f_code.co_filename
  249.         s = filename + '(' + `lineno` + ')'
  250.         if frame.f_code.co_name:
  251.             s = s + frame.f_code.co_name
  252.         else:
  253.             s = s + "<lambda>"
  254.         if frame.f_locals.has_key('__args__'):
  255.             args = frame.f_locals['__args__']
  256.             if args is not None:
  257.                 s = s + repr.repr(args)
  258.         if frame.f_locals.has_key('__return__'):
  259.             rv = frame.f_locals['__return__']
  260.             s = s + '->'
  261.             s = s + repr.repr(rv)
  262.         line = linecache.getline(filename, lineno)
  263.         if line: s = s + ': ' + string.strip(line)
  264.         return s
  265.     
  266.     # The following two methods can be called by clients to use
  267.     # a debugger to debug a statement, given as a string.
  268.     
  269.     def run(self, cmd):
  270.         import __main__
  271.         dict = __main__.__dict__
  272.         self.runctx(cmd, dict, dict)
  273.     
  274.     def runctx(self, cmd, globals, locals):
  275.         self.reset()
  276.         sys.settrace(self.trace_dispatch)
  277.         try:
  278.             try:
  279.                 exec(cmd + '\n', globals, locals)
  280.             except BdbQuit:
  281.                 pass
  282.         finally:
  283.             self.quitting = 1
  284.             sys.settrace(None)
  285.  
  286.     # This method is more useful to debug a single function call.
  287.  
  288.     def runcall(self, func, *args):
  289.         self.reset()
  290.         sys.settrace(self.trace_dispatch)
  291.         try:
  292.             try:
  293.                 apply(func, args)
  294.             except BdbQuit:
  295.                 pass
  296.         finally:
  297.             self.quitting = 1
  298.             sys.settrace(None)
  299.  
  300.  
  301. def set_trace():
  302.     Bdb().set_trace()
  303.  
  304. # -------------------- testing --------------------
  305.  
  306. class Tdb(Bdb):
  307.     def user_call(self, frame, args):
  308.         name = frame.f_code.co_name
  309.         if not name: name = '???'
  310.         print '+++ call', name, args
  311.     def user_line(self, frame):
  312.         import linecache, string
  313.         name = frame.f_code.co_name
  314.         if not name: name = '???'
  315.         fn = frame.f_code.co_filename
  316.         line = linecache.getline(fn, frame.f_lineno)
  317.         print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
  318.     def user_return(self, frame, retval):
  319.         print '+++ return', retval
  320.     def user_exception(self, frame, exc_stuff):
  321.         print '+++ exception', exc_stuff
  322.         self.set_continue()
  323.  
  324. def foo(n):
  325.     print 'foo(', n, ')'
  326.     x = bar(n*10)
  327.     print 'bar returned', x
  328.  
  329. def bar(a):
  330.     print 'bar(', a, ')'
  331.     return a/2
  332.  
  333. def test():
  334.     t = Tdb()
  335.     t.run('import bdb; bdb.foo(10)')
  336.